//
// This file is part of T-Rex, a Complex Event Processing Middleware.
// See http://home.dei.polimi.it/margara
//
// Authors: Alessandro Margara
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#include "CompositeEventGenerator.h"

CompositeEventGenerator::CompositeEventGenerator(CompositeEventTemplate *parCeTemplate) {
	ceTemplate = parCeTemplate->dup();
}

CompositeEventGenerator::~CompositeEventGenerator() {
	delete ceTemplate;
}

PubPkt * CompositeEventGenerator::generateCompositeEvent(PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters) {
	int eventType = ceTemplate->getEventType();
	int attributesNum = ceTemplate->getAttributesNum();
	Attribute attributes[attributesNum];
	for (int i=0; i<attributesNum; i++) {
		ceTemplate->getAttributeName(attributes[i].name, i);
		ValType valType = ceTemplate->getAttributeTree(i)->getValType();
		attributes[i].type = valType;
		if (valType==INT) attributes[i].intVal = computeIntValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, ceTemplate->getAttributeTree(i));
		else if (valType==FLOAT) attributes[i].floatVal = computeFloatValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, ceTemplate->getAttributeTree(i));
		else if (valType==BOOL) attributes[i].boolVal = computeBoolValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, ceTemplate->getAttributeTree(i));
		else if (valType==STRING) computeStringValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, ceTemplate->getAttributeTree(i), attributes[i].stringVal);
	}
	PubPkt *result = new PubPkt(eventType, attributes, attributesNum);
	result->setTime(partialEvent->indexes[0]->getTimeStamp());
	return result;
}

inline int CompositeEventGenerator::computeIntValue(PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters, OpTree *opTree) {
	OpTreeType type = opTree->getType();
	if (type==LEAF) {
		OpValueReference *reference = opTree->getValueReference();
		RulePktValueReference *pktReference = dynamic_cast<RulePktValueReference *>(reference);
		int index = pktReference->getIndex();
		bool refersToAgg = pktReference->refersToAgg();
		if (! refersToAgg) {
			PubPkt *pkt = partialEvent->indexes[index];
			int attrIndex;
			ValType type;
			pkt->getAttributeIndexAndType(pktReference->getName(), attrIndex, type);
			return pkt->getIntAttributeVal(attrIndex);
		} else {
			return computeAggregate(index, partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters);
		}
	} else {
		// Integer can only be obtained from integer: assume this is ensured at rule deployment time
		int leftValue = computeIntValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getLeftSubtree());
		int rightValue = computeIntValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getRightSubtree());
		OpTreeOperation op = opTree->getOp();
		if (op==ADD) return leftValue+rightValue;
		if (op==SUB) return leftValue-rightValue;
		if (op==MUL) return leftValue*rightValue;
		if (op==DIV) return leftValue/rightValue;
	}
	return 0;
}

inline float CompositeEventGenerator::computeFloatValue(PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters, OpTree *opTree) {
	OpTreeType type = opTree->getType();
	if (type==LEAF) {
		OpValueReference *reference = opTree->getValueReference();
		RulePktValueReference *pktReference = dynamic_cast<RulePktValueReference *>(reference);
		int index = pktReference->getIndex();
		bool refersToAgg = pktReference->refersToAgg();
		if (! refersToAgg) {
			PubPkt *pkt = partialEvent->indexes[index];
			int attrIndex;
			ValType type;
			pkt->getAttributeIndexAndType(pktReference->getName(), attrIndex, type);
			return pkt->getFloatAttributeVal(attrIndex);
		} else {
			return computeAggregate(index, partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters);
		}
	} else {
		// Floats can only be obtained from integer and float: assume this is ensured at rule deployment time
		float leftValue;
		if (opTree->getLeftSubtree()->getValType()==INT) leftValue = computeIntValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getLeftSubtree());
		else leftValue = computeFloatValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getLeftSubtree());
		float rightValue;
		if (opTree->getRightSubtree()->getValType()==INT) rightValue = computeIntValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getRightSubtree());
		else rightValue = computeFloatValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getRightSubtree());
		OpTreeOperation op = opTree->getOp();
		if (op==ADD) return leftValue+rightValue;
		if (op==SUB) return leftValue-rightValue;
		if (op==MUL) return leftValue*rightValue;
		if (op==DIV) return leftValue/rightValue;
	}
	return 0;
}

inline bool CompositeEventGenerator::computeBoolValue(PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters, OpTree *opTree) {
	OpTreeType type = opTree->getType();
	if (type==LEAF) {
		OpValueReference *reference = opTree->getValueReference();
		RulePktValueReference *pktReference = dynamic_cast<RulePktValueReference *>(reference);
		int index = pktReference->getIndex();
		bool refersToAgg = pktReference->refersToAgg();
		if (! refersToAgg) {
			PubPkt *pkt = partialEvent->indexes[index];
			int attrIndex;
			ValType type;
			pkt->getAttributeIndexAndType(pktReference->getName(), attrIndex, type);
			return pkt->getBoolAttributeVal(attrIndex);
		} else {
			// Aggregates not defines for type bool, up to now
			return false;
		}
	} else {
		// Booleans can only be obtained from booleans: assume this is ensured at rule deployment time
		bool leftValue = computeBoolValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getLeftSubtree());
		bool rightValue = computeBoolValue(partialEvent, aggregates, aggsSize, receivedPkts, receivedAggs, aggregateParameters, opTree->getRightSubtree());
		OpTreeOperation op = opTree->getOp();
		if (op==AND) return leftValue && rightValue;
		if (op==OR) return leftValue || rightValue;
	}
	return 0;
}

inline void CompositeEventGenerator::computeStringValue(PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters, OpTree *opTree, char *result) {
	// No operator is defined for strings: type can only be LEAF
	OpValueReference *reference = opTree->getValueReference();
	RulePktValueReference *pktReference = dynamic_cast<RulePktValueReference *>(reference);
	int index = pktReference->getIndex();
	bool refersToAgg = pktReference->refersToAgg();
	if (! refersToAgg) {
		PubPkt *pkt = partialEvent->indexes[index];
		int attrIndex;
		ValType type;
		pkt->getAttributeIndexAndType(pktReference->getName(), attrIndex, type);
		pkt->getStringAttributeVal(attrIndex, result);
	} else {
		// Aggregates not defines for type string, up to now
	}
}

inline int CompositeEventGenerator::computeAggregate(int index, PartialEvent *partialEvent, map<int, Aggregate *> &aggregates, int aggsSize[MAX_RULE_FIELDS], map<int, vector<PubPkt *> > &receivedPkts, map<int, vector<PubPkt *> > &receivedAggs, map<int, set<Parameter *> > &aggregateParameters) {
	Aggregate *agg = aggregates[index];
	TimeMs maxTS = partialEvent->indexes[agg->upperId]->getTimeStamp();
	TimeMs minTS = 0;
	if (agg->lowerId<0) {
		minTS = maxTS - agg->lowerTime;
	} else {
		minTS = partialEvent->indexes[agg->lowerId]->getTimeStamp();
	}
	int index1 = getFirstValidElement(receivedAggs[index], aggsSize[index], minTS);
	if (index1<0) return 0;
	int index2 = getLastValidElement(receivedAggs[index], aggsSize[index], maxTS, index1);
	if (index2<0) index2 = index1;
	AggregateFun fun = agg->fun;
	char *name = agg->name;
	int sum = 0;
	int count = 0;
	int min = 0;
	int max = 0;
	int attIndex;
	ValType type;
	bool checkParams = false;
	bool firstValue = true;
	map<int, set<Parameter *> >::iterator paramIt = aggregateParameters.find(index);
	if (paramIt!=aggregateParameters.end()) checkParams = true;
	for (int i=index1; i<=index2; i++) {
		PubPkt *pkt = receivedAggs[index][i];
		if (checkParams) {
			if (! checkParameters(pkt, partialEvent, paramIt->second)) continue;
		}
		int val = 0;
		if (pkt->getAttributeIndexAndType(name, attIndex, type) && type==INT) {
			val = pkt->getIntAttributeVal(attIndex);
		}
		count++;
		sum+=val;
		// First value
		if (firstValue) {
			min = val;
			max = val;
			firstValue = false;
			continue;
		}
		// Following values
		if (val<min) min=val;
		else if (val>max) max=val;
	}
	if (fun==SUM) return sum;
	if (fun==MAX) return max;
	if (fun==MIN) return min;
	if (fun==COUNT) return count;
	if (fun==AVG) {
		if (count==0) return 0;
		else return sum/count;
	}
	return 0;
}

bool CompositeEventGenerator::checkParameters(PubPkt *pkt, PartialEvent *partialEvent, set<Parameter *> &parameters) {
	for (set<Parameter *>::iterator it=parameters.begin(); it!=parameters.end(); ++it) {
		Parameter *parameter = *it;
		int indexOfReferenceEvent = parameter->evIndex1;
		PubPkt *receivedPkt = partialEvent->indexes[indexOfReferenceEvent];
		ValType type1, type2;
		int index1, index2;
		if (! receivedPkt->getAttributeIndexAndType(parameter->name1, index1, type1)) return false;
		if (! pkt->getAttributeIndexAndType(parameter->name2, index2, type2)) return false;
		if (type1!=INT || type2!=INT) return false;
		if (receivedPkt->getIntAttributeVal(index1)!=pkt->getIntAttributeVal(index2)) return false;
	}
	return true;
}
